import { BaseConnection, ConnectionStatus, WsServer } from "tsrpc";
import { GameMsgCall } from "./api/base";
import { ClientConnection, GameServer, gameServer } from "./server";
import { MsgDisconnect } from "./shared/gameClient/protocols/MsgDisconnect";
import { IUserInfo } from "./shared/gateClient/UserInfo";
import { hasProperty } from "./shared/tsgf/Utils";

declare module 'tsrpc' {
    export interface BaseConnection {
        userInfo: IUserInfo;
    }
}


/**连接认证通过前*/
export type connAuthingHandler = (conn: ClientConnection, reconnectOldId: string | null) => string | void;
/**连接认证通过后, 断开重连也会走这里*/
export type connAuthedHandler = (conn: ClientConnection, reconnectOldId: string | null) => void;
/**连接断开事件(非服务端主动断开),返回是否允许等待断线重连, 如果没人注册,则默认开启断线重连*/
export type connDicconnectHandler = (connId: string, userInfo: IUserInfo) => boolean;
/**用户断开连接并不再重连*/
export type userDisconnectHandler = (connId: string, userInfo: IUserInfo) => void;

/**游戏服务器的连接管理*/
export class GameConnMgr {

    /**等待断线重连的秒数(超过这个时间不再等待重连,即要求重连会被踢掉要求重新登陆)*/
    public waitReconnectSec=15;

    private connAuthingHandlers: connAuthingHandler[] = [];
    private connAuthedHandlers: connAuthedHandler[] = [];
    private connDiconnectHandlers: connDicconnectHandler[] = [];
    private userDisconnectHandlers: userDisconnectHandler[] = [];

    /**当前连接中的所有连接*/
    private currConnections: { [connId: string]: ClientConnection } = {};
    /**当前连接中的连接对应的用户信息*/
    private currConnectionUserInfo: { [connId: string]: IUserInfo } = {};
    /**等待断线重连的用户*/
    private waitConnectionUserInfo: { [connId: string]: IUserInfo } = {};
    /**等待断线重连的定时器*/
    private waitConnectionTimeHD: { [connId: string]: any } = {};
    /**所有用户信息(完全断开后才清除)*/
    private allConnectionUserInfo: { [connId: string]: IUserInfo } = {};

    private gameServer: GameServer;

    constructor(gameServer: GameServer) {
        this.gameServer = gameServer;


        //定义连接断开后的数据清理
        this.gameServer.flows.postDisconnectFlow.push(v => {
            var connId = v.conn.connectionId;
            var conn = this.currConnections[connId];
            var userInfo = this.currConnectionUserInfo[connId];
            //直接删除当前连接的相关数据, 不管有没重连, 都断开了
            delete this.currConnections[connId];
            delete this.currConnectionUserInfo[connId];
            if (!conn || !userInfo) return v;
            if (!this.triggerConnWaitReconnect(connId, userInfo)) {
                //如果有地方要求不走断线重连,则直接触发断开
                this.disconnectClearData(connId, userInfo);
                return v;
            }
            //开始等待断线重连流程
            this.startWaitReconnect(connId, userInfo);
            return v;
        });

        //定义所有消息请求都要再认证通过之后才会受理
        this.gameServer.flows.preApiCallFlow.push(call => {
            let conf = call.service.conf;
            if (call.service.name == "Reconnect" || call.service.name == "Login") {
                return call;
            }
            if (!call.conn.userInfo) {
                //发送所有消息前必须通过认证
                call.error('You need login before do this', { code: 4000 });
                return undefined;
            }
            return call;
        });
        this.gameServer.listenMsg('Disconnect', (call: GameMsgCall<MsgDisconnect>) => {
            //主动要求断开的,直接清理数据
            this.disconnectClearData(call.conn.connectionId, call.conn.userInfo);
            call.conn.close();
        });
    }


    /**连接断开的清理数据*/
    protected disconnectClearData(connId: string, userInfo: IUserInfo, triggerEvent: boolean = true): void {
        var hd = this.waitConnectionTimeHD[connId];
        if (hd) clearTimeout(hd);

        delete this.currConnections[connId];
        delete this.currConnectionUserInfo[connId];
        delete this.waitConnectionUserInfo[connId];
        delete this.waitConnectionTimeHD[connId];
        delete this.allConnectionUserInfo[connId];
        if (triggerEvent) this.triggerUserDisconnect(connId, userInfo);
    }
    /**开始等待断线重连流程*/
    protected startWaitReconnect(connId: string, userInfo: IUserInfo): void {
        var hd = this.waitConnectionTimeHD[connId];
        if (hd) clearTimeout(hd);
        this.waitConnectionUserInfo[connId] = userInfo;
        this.waitConnectionTimeHD[connId] = setTimeout(() => {
            this.disconnectClearData(connId, userInfo, true);
        }, this.waitReconnectSec * 1000);
    }
    /**尝试完成重连,清理等待数据,并返回这个连接之前用户信息,如果为undefined则表示已经不再等待重连,需要重新登陆*/
    protected tryReconnect(oldConnId: string): IUserInfo | undefined {
        var hd = this.waitConnectionTimeHD[oldConnId];
        if (hd) clearTimeout(hd);
        var waitUserInfo = this.waitConnectionUserInfo[oldConnId];
        delete this.waitConnectionUserInfo[oldConnId];
        delete this.waitConnectionTimeHD[oldConnId];
        delete this.allConnectionUserInfo[oldConnId];
        return waitUserInfo;
    }

    /**
     * 检查新连接通过认证,是否有阻止的
     * @param conn 
     * @param userInfo 
     */
    public checkConnAuthorizing(conn: ClientConnection, userInfo: IUserInfo, reconnectOldId: string | null = null): string | null {
        conn.userInfo = userInfo;
        return this.triggerConnAuthing(conn, reconnectOldId);
    }
    /**
     * 设置新连接通过授权认证
     * @param conn 
     * @param userInfo 
     * @param reconnectOldId 断线重连的旧的connId
     */
    public setConnAuthorized(conn: ClientConnection, userInfo: IUserInfo, reconnectOldId: string | null = null): void {
        conn.userInfo = userInfo;
        this.currConnections[conn.connectionId] = conn;
        this.currConnectionUserInfo[conn.connectionId] = userInfo;
        this.allConnectionUserInfo[conn.connectionId] = userInfo;
        this.triggerConnAuthed(conn, reconnectOldId);
    }
    /**
     * 连接重连
     * @param conn 
     * @param oldConnId 
     * @returns 成功null或者失败的错误消息 
     */
    public connReconnect(conn: ClientConnection, oldConnId: string): string | null {
        var oldUserInfo = this.tryReconnect(oldConnId);
        if (!oldUserInfo) return 'ReconnectId已经失效, 请重新登陆!';
        var err = this.checkConnAuthorizing(conn, oldUserInfo, oldConnId);
        if (err) {
            return err;
        }
        delete this.currConnectionUserInfo[oldConnId];
        this.setConnAuthorized(conn, oldUserInfo, oldConnId);
        return null;
    }
    /**
     * 当前是否还有用户(包含等待断线重连的)
     */
    public hasUser(): boolean {
        return hasProperty(this.allConnectionUserInfo);
    }
    /**
     * 获取用户信息
     */
    public getUserInfo(connId: string): IUserInfo | undefined {
        return this.allConnectionUserInfo[connId];
    }



    protected triggerConnAuthing(conn: ClientConnection, reconnectOldId: string | null): string | null {
        for (var i = 0; i < this.connAuthingHandlers.length; i++) {
            try {
                let err = this.connAuthingHandlers[i](conn, reconnectOldId);
                if (err) return err;
            } catch (e) {
                gameServer.logger?.error("triggerConnAuthed:", e);
            }
        }
        return null;
    }
    protected triggerConnAuthed(conn: ClientConnection, reconnectOldId: string | null): void {
        //延时1毫秒,让原始授权流程完整走完后再触发事件
        setTimeout(() => {
            for (var i = 0; i < this.connAuthedHandlers.length; i++) {
                try {
                    this.connAuthedHandlers[i](conn, reconnectOldId);
                } catch (e) {
                    gameServer.logger?.error("triggerConnAuthed:", e);
                }
            }
        }, 1);
    }
    protected triggerConnWaitReconnect(connId: string, userInfo: IUserInfo): boolean {
        for (var i = 0; i < this.connDiconnectHandlers.length; i++) {
            try {
                let wait = this.connDiconnectHandlers[i](connId, userInfo);
                if (!wait) return false;
            } catch (e) {
                gameServer.logger?.error("triggerConnWaitReconnect:", e);
            }
        }
        return true;
    }
    protected triggerUserDisconnect(connId: string, userInfo: IUserInfo): void {
        //延时1毫秒,让原始流程完整走完后再触发事件
        setTimeout(() => {
            for (var i = 0; i < this.userDisconnectHandlers.length; i++) {
                try {
                    this.userDisconnectHandlers[i](connId, userInfo);
                } catch (e) {
                    gameServer.logger?.error("triggerConnDisconnect:", e);
                }
            }
        }, 1);
    }

    /**
     * 注册验证连接授权事件,新连接和断线重连都走这里
     * @param handler 
     */
    public onConnAuthing(handler: connAuthingHandler): void {
        this.connAuthingHandlers.push(handler);
    }
    /**
     * 反注册连接授权事件
     * @param handler 
     */
    public offConnAuthing(handler: connAuthingHandler): void {
        this.connAuthingHandlers.remove(h => h == handler);
    }

    /**
     * 注册连接授权事件,新连接和断线重连成功都走这里
     * @param handler 
     */
    public onConnAuthed(handler: connAuthedHandler): void {
        this.connAuthedHandlers.push(handler);
    }
    /**
     * 反注册连接授权事件
     * @param handler 
     */
    public offConnAuthed(handler: connAuthedHandler): void {
        this.connAuthedHandlers.remove(h => h == handler);
    }

    /**
     * 注册连接断开事件(非服务端主动断开),返回是否允许等待断线重连, 如果要注册彻底断开则使用onUserDisconnect
     * @param handler 
     */
    public onConnDiconnect(handler: connDicconnectHandler): void {
        this.connDiconnectHandlers.push(handler);
    }
    /**
     * 反注册连接断开事件
     * @param handler 
     */
    public offConnDiconnect(handler: connDicconnectHandler): void {
        this.connDiconnectHandlers.remove(h => h == handler);
    }


    /**
     * 注册用户断开事件(连接断开并且不再等待重连后触发)
     * @param handler 
     */
    public onUserDisconnect(handler: userDisconnectHandler): void {
        this.userDisconnectHandlers.push(handler);
    }
    /**
     * 反注册用户断开事件
     * @param handler 
     */
    public offUserDisconnect(handler: userDisconnectHandler): void {
        this.userDisconnectHandlers.remove(h => h == handler);
    }


}